﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using RolePlayingGameData;
namespace WindowsGame2.Characters
{
    class PlayerObject
    {
        Player m_playerData;
        ContentManager m_content = null;
        Viewport m_viewport;
        Point m_tileSize = Point.Zero;
        Vector2 m_tileOffset = Vector2.Zero;
        Vector2 m_desiredMovement = Vector2.Zero;
        float m_movementSpeed = 1.5f;
        int m_sight = 3;
        bool m_isMoving = false;
        bool m_isSelected = false;
        bool m_isTargeted = false;
        AnimatingSprite m_primaryTargetSprite = new AnimatingSprite();
        AnimatingSprite m_secondaryTargetSprite = new AnimatingSprite();
        int m_tryPathFindingCollisionCount = 0;
        WindowsGame2.Weapon.SpellObject m_spellObject = new WindowsGame2.Weapon.SpellObject();

        public List<WindowsGame2.Map.TILE_POSITION> m_waypointContainer;

        public Player PlayerData
        {
            get { return m_playerData; }
        }

        public Point TilePosition
        {
            get { return m_playerData.MapPosition; }
        }

        public Vector2 TileOffset
        {
            get { return m_tileOffset; }
        }

        public Vector2 DesiredMovement
        {
            get { return m_desiredMovement; }
            set { m_desiredMovement = value; }
        }

        public bool IsSelected
        {
            get { return m_isSelected; }
            set { m_isSelected = value; }
        }

        public bool IsTargeted
        {
            get { return m_isTargeted; }
            set { m_isTargeted = value; }
        }

        public void Attack(ref Vector2 mapOriginPosition, WindowsGame2.Characters.PlayerObject target)
        {
            m_spellObject.SetTarget(GetWorldPosition(), target);
            target.IsTargeted = target == null ? false : true;
        }

        public int Sight
        {
            get { return m_sight; }
        }
       
        public void Initialize(ContentManager content, Viewport viewport, Point tileSize)
        {
            m_content = content;
            m_viewport = viewport;
            m_tileSize = tileSize;
            
            m_spellObject.Initialize(content, viewport);            
        }

        public void LoadContent(string contentName, Point spawnMapPosition)
        {
            m_playerData = m_content.Load<Player>(Path.Combine(@"Characters\Players", contentName)).Clone() as Player;
            m_playerData.MapPosition = spawnMapPosition;
            m_playerData.Direction = Direction.South;

            Point frameDimensions = new Point(102, 54);
            
            m_primaryTargetSprite.FramesPerRow = 4;
            m_primaryTargetSprite.FrameDimensions = frameDimensions;
            m_primaryTargetSprite.AddAnimation(
                new Animation("Selection", 1, 4, 100, true));
            m_primaryTargetSprite.PlayAnimation(0);
            m_primaryTargetSprite.SourceOffset =
                new Vector2(18, -16);
            m_primaryTargetSprite.Texture =
                m_content.Load<Texture2D>(@"Textures\Combat\Target1SelectionRing");

            m_secondaryTargetSprite.FramesPerRow = 4;
            m_secondaryTargetSprite.FrameDimensions = frameDimensions;
            m_secondaryTargetSprite.AddAnimation(
                new Animation("Selection", 1, 4, 100, true));
            m_secondaryTargetSprite.PlayAnimation(0);
            m_secondaryTargetSprite.SourceOffset =
                new Vector2(18, -16);
            m_secondaryTargetSprite.Texture =
                m_content.Load<Texture2D>(@"Textures\Combat\Target2SelectionRing");

            m_spellObject.LoadContent("FireBolt");
        }

        public void Draw(SpriteBatch spriteBatch, float elapsedGameTimeInSeconds, Vector2 mapOriginPosition)
        {
            Vector2 screenPosition = GetScreenPosition(mapOriginPosition);
            
            if (m_isSelected)
            {
                m_primaryTargetSprite.UpdateAnimation(elapsedGameTimeInSeconds);
                m_primaryTargetSprite.Draw(spriteBatch, screenPosition, 1f - screenPosition.Y / (float)m_viewport.Height);
            }
            else if (m_isTargeted)
            {
                m_secondaryTargetSprite.UpdateAnimation(elapsedGameTimeInSeconds);
                m_secondaryTargetSprite.Draw(spriteBatch, screenPosition, 1f - screenPosition.Y / (float)m_viewport.Height);
            }

            m_playerData.ResetAnimation(m_isMoving);
            switch (m_playerData.State)
            {
                case Character.CharacterState.Idle:
                    if (m_playerData.MapSprite != null)
                    {                       
                        m_playerData.MapSprite.UpdateAnimation(elapsedGameTimeInSeconds);
                        m_playerData.MapSprite.Draw(spriteBatch, screenPosition,
                            1f - screenPosition.Y / (float)m_viewport.Height);

                        if (m_playerData.CharacterClass.LevelEntries[0].Spells.Count > 0)
                        {
                                                
                        }
                    }
                    break;

                case Character.CharacterState.Walking:
                    if (m_playerData.WalkingSprite != null)
                    {
                        m_playerData.WalkingSprite.UpdateAnimation(elapsedGameTimeInSeconds);
                        m_playerData.WalkingSprite.Draw(spriteBatch, screenPosition,
                            1f - screenPosition.Y / (float)m_viewport.Height);
                    }
                    else if (m_playerData.MapSprite != null)
                    {
                        m_playerData.MapSprite.UpdateAnimation(elapsedGameTimeInSeconds);
                        m_playerData.MapSprite.Draw(spriteBatch, screenPosition,
                            1f - screenPosition.Y / (float)m_viewport.Height);
                    }
                    break;
            }

            m_spellObject.Draw(spriteBatch, elapsedGameTimeInSeconds, mapOriginPosition);
        }

        public void Update(WindowsGame2.Map.MapObject mapObject, float elapsedGameTimeInSeconds)
        {
            m_spellObject.Update(elapsedGameTimeInSeconds);

            if (DesiredMovement == Vector2.Zero)
            {
                if (m_waypointContainer != null && m_waypointContainer.Count > 0)
                {
                    DesiredMovement = Vector2.Multiply(
                            new Vector2(m_tileSize.X, m_tileSize.Y), new Vector2(
                            m_waypointContainer[0].m_x -
                                TilePosition.X,
                            m_waypointContainer[0].m_y -
                                TilePosition.Y));

                    m_waypointContainer.RemoveAt(0);
                    if (DesiredMovement == Vector2.Zero) return;
            
                }
                else
                {
                    Move(Vector2.Zero, mapObject);
                    return;
                }
            }

            // get the remaining-movement direction
            Vector2 autoMovementDirection = Vector2.Normalize(DesiredMovement);

            // calculate the potential movement vector
            Vector2 movement = Vector2.Multiply(autoMovementDirection,m_movementSpeed);

            // limit the potential movement vector by the remaining auto-movement
            movement.X = Math.Sign(movement.X) * Math.Min(Math.Abs(movement.X),
                Math.Abs(DesiredMovement.X));
            movement.Y = Math.Sign(movement.Y) * Math.Min(Math.Abs(movement.Y),
                Math.Abs(DesiredMovement.Y));

            // remove the movement from the total remaining auto-movement
            Move(movement, mapObject);

            DesiredMovement -= movement;           
        }

        public bool IsInPoint(Vector2 worldPosition)
        {
            Vector2 worldPositionOfPlayer = GetWorldPosition();

            if (worldPositionOfPlayer.X > worldPosition.X || worldPositionOfPlayer.X + 64 < worldPosition.X) return false;
            if (worldPositionOfPlayer.Y > worldPosition.Y || worldPositionOfPlayer.Y + 64 < worldPosition.Y) return false;
            
            return true;

        }

        void Move(Vector2 movement, WindowsGame2.Map.MapObject mapObject)
        {
            m_isMoving = (movement != Vector2.Zero);            

            // if the position is moving, up the direction
            if (m_isMoving == false) return;
            
            mapObject.UnoccupiedTilePositionByDynamicObject(m_playerData.MapPosition);

            if (m_tryPathFindingCollisionCount > 0)
            {
                Vector2 dir;
                Vector2.Normalize(ref movement, out dir);
                    
                movement.X = dir.Y * m_movementSpeed;
                movement.Y = dir.X * m_movementSpeed;
                    
            }

            Point tilePosition = m_playerData.MapPosition;
            Vector2 tileOffset = m_tileOffset;
            CalculateMovement(movement, ref tilePosition, ref tileOffset);
                

            if (mapObject.IsOccupiedTilePositionByDynamicObject(tilePosition) == false)
            {   
                m_playerData.MapPosition = tilePosition;
                m_tileOffset = tileOffset;
                m_playerData.Direction = CalculateDirection(movement);
                m_tryPathFindingCollisionCount = 0;
            }
            else
            {
                if (m_waypointContainer.Count > 0)
                {
                    if (m_waypointContainer[0].m_x != tilePosition.X || m_waypointContainer[0].m_y != tilePosition.Y)
                    {
                        ++m_tryPathFindingCollisionCount;
                    }                   
                }
                else
                {
                    ++m_tryPathFindingCollisionCount;
                }
            }
            mapObject.OccupiedTilePositionByDynamicObject(m_playerData.MapPosition, Sight);
            
        }

        void CalculateMovement(Vector2 movement, ref Point TilePosition, ref Vector2 TileOffset)
        {
            // add the movement
            TileOffset += movement;

            while (TileOffset.X > m_tileSize.X / 2f)
            {
                TilePosition.X++;
                TileOffset.X -= m_tileSize.X;
            }
            while (TileOffset.X < -m_tileSize.X / 2f)
            {
                TilePosition.X--;
                TileOffset.X += m_tileSize.X;
            }
            while (TileOffset.Y > m_tileSize.Y / 2f)
            {
                TilePosition.Y++;
                TileOffset.Y -= m_tileSize.Y;
            }
            while (TileOffset.Y < -m_tileSize.Y / 2f)
            {
                TilePosition.Y--;
                TileOffset.Y += m_tileSize.Y;
            }
        }

        Direction CalculateDirection(Vector2 vector)
        {
            if (vector.X > 0)
            {
                if (vector.Y > 0)
                {
                    return Direction.SouthEast;
                }
                else if (vector.Y < 0)
                {
                    return Direction.NorthEast;
                }
                else // y == 0
                {
                    return Direction.East;
                }
            }
            else if (vector.X < 0)
            {
                if (vector.Y > 0)
                {
                    return Direction.SouthWest;
                }
                else if (vector.Y < 0)
                {
                    return Direction.NorthWest;
                }
                else // y == 0
                {
                    return Direction.West;
                }
            }
            else // x == 0
            {
                if (vector.Y > 0)
                {
                    return Direction.South;
                }
                else if (vector.Y < 0)
                {
                    return Direction.North;
                }
            }
            // x == 0 && y == 0, so... south?
            return Direction.South;
        }

        Vector2 GetScreenPosition(Vector2 mapOriginPosition)
        {
            Vector2 screenPosition = new Vector2(
                mapOriginPosition.X + TilePosition.X * m_tileSize.X,
                mapOriginPosition.Y + TilePosition.Y * m_tileSize.Y) + TileOffset;

            return screenPosition;
        }

        public Vector2 GetWorldPosition()
        {
            Vector2 screenPosition = new Vector2(
                TilePosition.X * m_tileSize.X,
                TilePosition.Y * m_tileSize.Y) + TileOffset;

            return screenPosition;
        }
    }
}
